#include <iostream>
#include "Client2.h"
#include "Buffer.h"
#include "Mutex.h"
#include "Event.h"

Client::Client(IEmpacter *empacter, IBuffer *buffer, unsigned int bufferReadSize) :
_empacter(empacter),
_buffer(buffer),
_bufferReadSize(bufferReadSize),
_id(-1),
_availableToSend(0),
_sendLoop(true),
_isConnected(false)
{
	this->_buffSend = new Buffer(20000);
	this->_lockSend = new Mutex();
	this->_eventBuffer = new Event();
	this->_eventDestructor = new Event();

	ThreadPool::Request *rqt = new ThreadPool::Request();
	rqt->function = &Client::SendData;
	rqt->autoFree = true;
	rqt->parameters = this;
	ThreadPool::GetThreadPool()->AddRequest(rqt);
}

Client::~Client()
{
	this->_sendLoop = false;
	this->_eventBuffer->Set();
	this->_eventDestructor->Wait();
	delete this->_buffSend;
	delete this->_lockSend;
	delete this->_eventBuffer;
	delete this->_eventDestructor;
	delete _connection;
}

int Client::Connect(std::string adress, int port)
{
	this->_connection = new Network::Guest();
	if (this->_connection->Init())
		return (-1);
	this->_connection->Connect(adress, port);
	if (this->Authentificate() >= 0)
	{
		ThreadPool::Request *rqt = new ThreadPool::Request();
		rqt->function = &Client::SelectReceive;
		rqt->autoFree = true;
		rqt->parameters = this;
		ThreadPool::GetThreadPool()->AddRequest(rqt);
		this->_isConnected = true;
	}
	else
	{
		this->Disconnect();
		return (-1);
	}
	return (0);
}

void Client::SelectReceive(void* data)
{
	Client *client = reinterpret_cast<Client*>(data);
	unsigned int len;
	unsigned int available;
	char *buff = new char[client->_bufferReadSize];

	while(1)
	{
		available = client->_buffer->SizeMax() - client->_buffer->Size();

		len = available > client->_bufferReadSize ? client->_bufferReadSize : available;
		if (client->_connection->ReceiveData(buff, &len, 0) == 0)
		{
			if (len > 0)
			{
					client->_buffer->Write(buff, len);
					if (client->_buffer->Size() > 50000)
					{
						exit(0);
					}
			}
			else
			{
				std::cout << "error receive" << std::endl;
				client->Disconnect();
				break;
			}
		}
		else
		{
			std::cout << "error select" << std::endl;
			client->Disconnect();
			break;
		}
	}
	delete buff;
}

bool			Client::IsConnected()
{
	return (this->_isConnected);
}

int Client::Disconnect()
{
	this->_isConnected = false;
	_buffer->Clean();
	_bufferReadSize = 0;
	_id = -1;
	_availableToSend = 0;
/*	delete _buffSend;
	delete _lockSend;
	delete _eventBuffer;
	delete _eventDestructor;*/
	return (this->_connection->CloseSocket());
}

int	Client::Authentificate()
{
	char	buff[sizeof(int)];
	unsigned int res;
	unsigned int sizedp;
	timeval tm;

	tm.tv_sec = 1;
	tm.tv_usec = 0;

	sizedp = sizeof(int);
	res = this->_connection->ReceiveData(buff, &sizedp, &tm);
	if (res)
		return (-1);
	if (sizedp == sizeof(int))
		return (this->_id = (int)*buff);
	return (-1);
}

int	Client::GetId()
{
	return (this->_id);
}


//-------------------------------------J
unsigned int	Client::SendBuffer(void* data, unsigned int size)
{
	this->_lockSend->Lock();
	if (this->_buffSend->SizeMax() - this->_buffSend->Size() < size)
	{
		this->_lockSend->Unlock();
		return (-1);
	}
	if (this->_buffSend->Write(data, size) >= 0)
	{
		this->_availableToSend += size;
		this->_eventBuffer->Set();
	}
	this->_lockSend->Unlock();
	return (0);
}

void	Client::SendData(void* client)
{
	Client*			clt = reinterpret_cast<Client*>(client);
	unsigned int	size;
	unsigned int	sentSize;
	void*			data;
	timeval			tm;


	while (clt->_sendLoop)
	{
		clt->_eventBuffer->Wait();
		clt->_lockSend->Lock();
		size = clt->_availableToSend;
		clt->_lockSend->Unlock();
		data = clt->_buffSend->Read(size, false);
		tm.tv_sec = 1;
		tm.tv_usec = 0;
		sentSize = size;
		clt->_connection->SendData(data, &sentSize, &tm);
		
		clt->_lockSend->Lock();
		if (sentSize == clt->_availableToSend)
		{
			clt->_eventBuffer->Reset();
		}
		clt->_availableToSend -= sentSize;
		clt->_buffSend->Delete(sentSize);
		clt->_lockSend->Unlock();
	}
	clt->_eventDestructor->Set();
}

//-------------------------------------J
